package net.quanter.shield.utils.maths;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;

/**
 * 数字工具类
 *
 * @author 王老实
 * @since 1.3.12.RELEASE
 */
public class MathUtils {

    private static final Random r = new Random(System.currentTimeMillis());
    private static final int D = 3;

    /**
     * 求最大最小值函数
     *
     * @param d 数组
     * @return [0]:最大值 [1]:最小值
     */
    public final static double[] getMaxMin(double... d) {
        double[] result = {Double.MIN_VALUE, Double.MAX_VALUE};
        for (int i = 0; i < d.length; i++) {
            if (d[i] > result[0]) {
                result[0] = d[i];
            }
            if (d[i] < result[1]) {
                result[1] = d[i];
            }
        }
        return result;
    }

    /**
     * 求最大值函数
     *
     * @param d 数组
     * @return 最大值
     */
    public final static double max(double... d) {
        return getMaxMin(d)[0];
    }

    /**
     * 求最小值函数
     *
     * @param d 数组
     * @return 最小值
     */
    public final static double min(double... d) {
        return getMaxMin(d)[1];
    }

    /**
     * 求和函数
     *
     * @param a 求和的数组
     * @return 和
     */
    public final static double sum(double[] a) {
        if (a == null) {
            return Double.MIN_VALUE;
        }
        double sum = 0;
        for (int i = 0; i < a.length; i++) {
            sum += a[i];
        }
        return sum;
    }


    /**
     * 求平均函数
     *
     * @param a 求平均的数组
     * @return 平均数
     */
    public final static double avg(double[] a) {
        if (a == null) {
            return 0;
        }
        double sum = 0;
        for (int i = 0; i < a.length; i++) {
            sum += a[i];
        }
        return sum / a.length;
    }

    /**
     * 求标准差函数,标准差公式
     * ⒈方差s^2=[(x1-x)^2+(x2-x)^2+......(xn-x)^2]/(n) （x为平均数）
     * ⒉标准差=方差的算术平方根
     *
     * @param a 求标准差的数组
     * @return 标准差
     */
    public final static double std(double[] a) {
        if (a == null) {
            return 0;
        }
        double avg = avg(a);
        int num = a.length;
        double ma = 0;
        for (int i = 0; i < num; i++) {
            ma += Math.pow(a[i] - avg, 2);
        }
        double sd = Math.sqrt(ma / num);
        return sd;
    }

    /**
     * 返回不大于参数的随机数
     *
     * @param max 随机数最大数
     * @return 随机数
     */
    public final static int getRandom(int max) {
        return r.nextInt(max);
    }


    /**
     * 正太分布函数
     *
     * @param u 正态分布数
     * @return 正态分布
     */
    public static double gaussian(double u) {
        double y = Math.abs(u);
        double y2 = y * y;
        double z = Math.exp(-0.5 * y2) * 0.398942280401432678;
        double p = 0;
        int k = 28;
        double s = -1;
        double fj = k;
        //当y>3时
        if (y > D) {
            for (int i = 1; i <= k; i++) {
                p = fj / (y + p);
                fj = fj - 1.0;
            }
            p = z / (y + p);
        } else {
            //当y<3时
            for (int i = 1; i <= k; i++) {
                p = fj * y2 / (2.0 * fj + 1.0 + s * p);
                s = -s;
                fj = fj - 1.0;
            }
            p = 0.5 - z * y / (1 - p);
        }
        if (u > 0) {
            p = 1.0 - p;
        }
        return p;
    }

    final static double[] fp1 = new double[]{50};
    final static double[] fp2 = new double[]{50, 50};

    /**
     * 根据数组个数计算斐波拉起数列
     * 普通版，斐波拉起数列是
     * 1 1 2 3 5 8...
     *
     * @param m  数组个数
     * @param a0 第一个元素大小
     * @param a1 第二个元素大小
     * @return 斐波拉契数列
     */
    public static double[] fibonacciPersent(int m, int a0, int a1) {
        //不能小于3
        if (m <= 1) {
            return fp1;
        } else if (m == 2) {
            return fp2;
        }
        int[] a = fibonacci(m, a0, a1);
        //转换成百分比后的斐波拉契数列
        double[] b = new double[a.length];
        double sum = 0;
        for (int i = 0; i < a.length; i++) {
            sum += a[i];
        }
        for (int i = 0; i < b.length; i++) {
            b[i] = a[i] / sum;
        }
        return b;
    }

    /**
     * 根据数组个数计算斐波拉起数列
     *
     * @param m  数组个数
     * @param a0 第一个元素大小
     * @param a1 第二个元素大小
     * @return 斐波拉契数列
     */
    public static int[] fibonacci(int m, int a0, int a1) {
        //不能小于3
        if (m <= 1) {
            return new int[]{a0};
        } else if (m == 2) {
            return new int[]{a0, a1};
        }
        //原始斐波拉契数列
        int[] a = new int[m];
        a[0] = a0;
        a[1] = a1;
        for (int i = 2; i < m; i++) {
            a[i] = a[i - 1] + a[i - 2];
        }
        return a;
    }

    /**
     * 四舍五入取，返回最后指定长度
     *
     * @param x    [1213.5556 1.431232]
     * @param size 6
     * @return [1213.56 1.43123]
     */
    public static double round(double x, int size) {
        String s = String.valueOf(x);
        if (s.trim().length() < size + 1) {
            return x;
        }
        int point_index = s.indexOf('.');
        if (point_index < 1) {
            return new BigDecimal(x).setScale(0, RoundingMode.HALF_UP).doubleValue();
        } else {
            return new BigDecimal(x).setScale(size - point_index, RoundingMode.HALF_UP).doubleValue();
        }
    }

    /**
     * 向下取整
     *
     * @param x 原始数 [31.2,43.8]
     * @param d 精度  0.5
     * @return [31, 43.5]
     */
    public static double floor(double x, double d) {
        int a = (int) (x / d);
        double b = a * d;
        return b;
    }

    /**
     * 向上取整
     *
     * @param x 原始数 [31.2,43.8]
     * @param d 精度  0.5
     * @return [31.5, 44]
     */
    public static double ceil(double x, double d) {
        int a = (int) (x / d);
        double m = x % d;
        double b = (a + (m == 0 ? 0 : 1)) * d;
        return b;
    }

    /**
     * 向下取整
     *
     * @param x 原始数 [31,143]
     * @param d 精度  100
     * @return [0, 100]
     */
    public static int floor(int x, int d) {
        int a = x / d;
        int b = a * d;
        return b;
    }

    /**
     * 向上取证
     *
     * @param x [21,178]
     * @param d 100
     * @return [100, 200]
     */
    public static int ceil(int x, int d) {
        int a = x / d;
        int m = x % d;
        int b = (a + (m == 0 ? 0 : 1)) * d;
        return b;
    }
}
